/*
** 2004 Dec 29
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code that is specific to RISCOS systems.
*/
#include "sqliteInt.h"
#include "os.h"
#if OS_RISCOS              /* This file is used on RISC OS only */


#include <time.h>
#include <errno.h>
#include <stdio.h>
#include "osfile.h"
#include "osfscontrol.h"
#include "osfind.h"
#include "osgbpb.h"
#include "osargs.h"
#include "kernel.h"


/*
** Include code that is common to all os_*.c files
*/
#include "os_common.h"

/*
** The riscosFile structure is subclass of OsFile specific for the riscos
** protability layer.
*/
typedef struct riscosFile riscosFile;
struct riscosFile {
  IoMethod const *pMethod;  /* Always the first entry */
  int h;                    /* The file descriptor */
  unsigned char isOpen;     /* True if needs to be closed */
  int delOnClose;           /* True if file is to be deleted on close */
  int locked;
  int locktype;
  char *pathToDel;          /* Name of file to delete on close */
  i64 offset;               /* Seek offset */
};

static int allocateRISCOSFile(riscosFile *pInit, OsFile **pId);

/*
** Delete the named file
*/
int sqlite3RISCOSDelete(const char *zFilename){
  _kernel_swi_regs regs;
  regs.r[0] = OSFile_Delete;
  regs.r[1] = (int)zFilename;
  _kernel_swi(XOS_File,&regs,&regs);
  return SQLITE_OK;
}

/*
** Return TRUE if the named file exists.
*/
int sqlite3RISCOSFileExists(const char *zFilename){
  _kernel_swi_regs regs;
  regs.r[0] = OSFile_ReadStampedNoPath;
  regs.r[1] = (int)zFilename;

  if (_kernel_swi(XOS_File,&regs,&regs) != NULL) {
    return 0;
    }

  if (regs.r[0] == osfile_NOT_FOUND) {
    return 0;
    }

  return 1;
}

/*
** Attempt to open a file for both reading and writing.  If that
** fails, try opening it read-only.  If the file does not exist,
** try to create it.
**
** On success, a handle for the open file is written to *id
** and *pReadonly is set to 0 if the file was opened for reading and
** writing or 1 if the file was opened read-only.  The function returns
** SQLITE_OK.
**
** On failure, the function returns SQLITE_CANTOPEN and leaves
** *id and *pReadonly unchanged.
*/
int sqlite3RISCOSOpenReadWrite(
  const char *zFilename,
  OsFile **pId,
  int *pReadonly
){
  riscosFile f;

  _kernel_oserror *err;
  _kernel_swi_regs regs;

  if( sqlite3RISCOSFileExists(zFilename)==0 ){
    regs.r[0] = OSFind_Openout | osfind_NO_PATH;
    regs.r[1] = (int)zFilename;

    err = _kernel_swi(OS_Find,&regs,&regs);
/*    printf("%d %s\n",err->errnum,err->errmess);*/
    }
  else {
    regs.r[0] = OSFind_Openup | osfind_NO_PATH;
    regs.r[1] = (int)zFilename;

    err = _kernel_swi(OS_Find,&regs,&regs);
/*    printf("%d %s\n",err->errnum,err->errmess);*/
    }

  f.h = regs.r[0];
  f.delOnClose = 0;

  if( f.h == 0 ){
    regs.r[0] = OSFind_Openin | osfind_NO_PATH;
    regs.r[1] = (int)zFilename;

    err = _kernel_swi(OS_Find,&regs,&regs);
/*    printf("%d %s\n",err->errnum,err->errmess);*/

    f.h = regs.r[0];

    if( f.h==0 ){
      return SQLITE_CANTOPEN;
    }
    *pReadonly = 1;
  }else{
    *pReadonly = 0;
  }
  TRACE3("OPEN    %-3d %s\n", f.h, zFilename);
  return allocateRISCOSFile(&f, pId);
}


/*
** Attempt to open a new file for exclusive access by this process.
** The file will be opened for both reading and writing.  To avoid
** a potential security problem, we do not allow the file to have
** previously existed.  Nor do we allow the file to be a symbolic
** link.
**
** If delFlag is true, then make arrangements to automatically delete
** the file when it is closed.
**
** On success, write the file handle into *id and return SQLITE_OK.
**
** On failure, return SQLITE_CANTOPEN.
*/
int sqlite3RISCOSOpenExclusive(const char *zFilename, OsFile **id, int delFlag){
  riscosFile f;

  _kernel_swi_regs regs;

  if( sqlite3RISCOSFileExists(zFilename)==1 ){
    return SQLITE_CANTOPEN;
  }

  regs.r[0] = OSFind_Openout | osfind_NO_PATH;
  regs.r[1] = (int)zFilename;

  _kernel_swi(OS_Find,&regs,&regs);

  f.h = regs.r[0];

  if( f.h ==0 ){
    return SQLITE_CANTOPEN;
  }
  f.delOnClose = delFlag;
  if (delFlag) {
    f.pathToDel = sqlite3RISCOSFullPathname(zFilename);
  }
  TRACE3("OPEN-EX %-3d %s\n", f.h, zFilename);
  return allocateRISCOSFile(&f, id);
}

/*
** Attempt to open a new file for read-only access.
**
** On success, write the file handle into *id and return SQLITE_OK.
**
** On failure, return SQLITE_CANTOPEN.
*/
int sqlite3RISCOSOpenReadOnly(const char *zFilename, OsFile **id){
  riscosFile f;

  _kernel_swi_regs regs;

  regs.r[0] = OSFind_Openin | osfind_NO_PATH;
  regs.r[1] = (int)zFilename;

  _kernel_swi(OS_Find,&regs,&regs);

  f.h = regs.r[0];
  f.delOnClose = 0;

  if( f.h ==0 ){
    return SQLITE_CANTOPEN;
  }
  f.locked = 0;
  TRACE3("OPEN-RO %-3d %s\n", f.h, zFilename);
  return allocateRISCOSFile(&f, id);
}

/*
** Attempt to open a file descriptor for the directory that contains a
** file.  This file descriptor can be used to fsync() the directory
** in order to make sure the creation of a new file is actually written
** to disk.
**
** This routine is not meaningful for RISCOS.
**
** On success, a handle for a previously open file is at *id is
** updated with the new directory file descriptor and SQLITE_OK is
** returned.
**
** On failure, the function returns SQLITE_CANTOPEN and leaves
** *id unchanged.
*/
int riscosOpenDirectory(
  const char *zDirname,
  OsFile **id
){
  return SQLITE_OK;
}

/*
** If the following global variable points to a string which is the
** name of a directory, then that directory will be used to store
** temporary files.
*/
char *sqlite3_temp_directory = 0;

/*
** Create a temporary file name in zBuf.  zBuf must be big enough to
** hold at least SQLITE_TEMPNAME_SIZE characters.
*/
int sqlite3RISCOSTempFileName(char *zBuf){
  static char zChars[] =
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789";
  int i, j;
  do{
    sprintf(zBuf, "<Wimp$ScrapDir>.%s",TEMP_FILE_PREFIX);
    j = strlen(zBuf);
    sqlite3Randomness(5, &zBuf[j]);
    for(i=0; i<5; i++, j++){
      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
    }
    zBuf[j] = 0;
  }while( sqlite3RISCOSFileExists(zBuf)==1 );
  return SQLITE_OK;
}

/*
** Read data from a file into a buffer.  Return SQLITE_OK if all
** bytes were read successfully and SQLITE_IOERR if anything goes
** wrong.
*/
int riscosRead(OsFile *id, void *pBuf, int amt){
  _kernel_swi_regs regs;

  regs.r[0] = OSGBPB_Read;
  regs.r[1] = ((riscosFile *)id)->h;
  regs.r[2] = (int)pBuf;
  regs.r[3] = amt;

  _kernel_swi(OS_GBPB,&regs,&regs);

  if( regs.r[3]==0 ){
    return SQLITE_OK;
  }else{
    return SQLITE_IOERR;
  }
}

/*
** Write data from a buffer into a file.  Return SQLITE_OK on success
** or some other error code on failure.
*/
int riscosWrite(OsFile *id, const void *pBuf, int amt){
  _kernel_swi_regs regs;

  regs.r[0] = OSGBPB_Write;
  regs.r[1] = ((riscosFile *)id)->h;
  regs.r[2] = (int)pBuf;
  regs.r[3] = amt;

  _kernel_swi(OS_GBPB,&regs,&regs);

  if( regs.r[3]==0 ){
    return SQLITE_OK;
  }else{
    return SQLITE_IOERR;
  }
}

/*
** Move the read/write pointer in a file.
*/
int riscosSeek(OsFile *id, i64 offset){
  _kernel_swi_regs regs;

  regs.r[0] = OSArgs_SetPtr;
  regs.r[1] = ((riscosFile *)id)->h;
  regs.r[2] = offset;

  _kernel_swi(OS_Args,&regs,&regs);
  return SQLITE_OK;
}

/*
** Make sure all writes to a particular file are committed to disk.
**
** Under Unix, also make sure that the directory entry for the file
** has been created by fsync-ing the directory that contains the file.
** If we do not do this and we encounter a power failure, the directory
** entry for the journal might not exist after we reboot.  The next
** SQLite to access the file will not know that the journal exists (because
** the directory entry for the journal was never created) and the transaction
** will not roll back - possibly leading to database corruption.
*/
int riscosSync(OsFile *id,int i){
  return SQLITE_OK;
}

/*
** Sync the directory zDirname. This is a no-op on operating systems other
** than UNIX.
*/
int sqlite3RISCOSSyncDirectory(const char *zDirname){
  return SQLITE_OK;
}

/*
** Truncate an open file to a specified size
*/
int riscosTruncate(OsFile *id, i64 nByte){
  _kernel_swi_regs regs;

  regs.r[0] = OSArgs_SetExt;
  regs.r[1] = ((riscosFile *)id)->h;
  regs.r[2] = 0;

  if (_kernel_swi(XOS_Args,&regs,&regs) != NULL) {
    return SQLITE_IOERR;
    }

  return SQLITE_OK;
}

/*
** Determine the current size of a file in bytes
*/
int riscosFileSize(OsFile *id, i64 *pSize){
  _kernel_swi_regs regs;
_kernel_oserror *err;

  regs.r[0] = OSArgs_ReadExt;
  regs.r[1] = ((riscosFile *)id)->h;

  if ((err = _kernel_swi(XOS_Args,&regs,&regs)) != NULL) {
    *pSize = 0;
    return SQLITE_IOERR;
    }

  *pSize = regs.r[2];

  return SQLITE_OK;
}

/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero.  If the file is unlocked or holds only SHARED locks, then
** return zero.
*/
int riscosCheckReservedLock(OsFile *id){
  return 0;
}

/*
** Lock the file with the lock specified by parameter locktype - one
** of the following:
**
**     (1) SHARED_LOCK
**     (2) RESERVED_LOCK
**     (3) PENDING_LOCK
**     (4) EXCLUSIVE_LOCK
**
** Sometimes when requesting one lock state, additional lock states
** are inserted in between.  The locking might fail on one of the later
** transitions leaving the lock state different from what it started but
** still short of its goal.  The following chart shows the allowed
** transitions and the inserted intermediate states:
**
**    UNLOCKED -> SHARED
**    SHARED -> RESERVED
**    SHARED -> (PENDING) -> EXCLUSIVE
**    RESERVED -> (PENDING) -> EXCLUSIVE
**    PENDING -> EXCLUSIVE
**
** This routine will only increase a lock.  Use the sqlite3RISCOSUnlock()
** routine to lower a locking level.
*/
int riscosLock(OsFile *id, int locktype){
  return SQLITE_OK;
}

/*
** Lower the locking level on file descriptor id to locktype.  locktype
** must be either NO_LOCK or SHARED_LOCK.
**
** If the locking level of the file descriptor is already at or below
** the requested locking level, this routine is a no-op.
**
** It is not possible for this routine to fail if the second argument
** is NO_LOCK.  If the second argument is SHARED_LOCK, this routine
** might return SQLITE_IOERR instead of SQLITE_OK.
*/
int riscosUnlock(OsFile *id, int locktype){
  return SQLITE_OK;
}

/*
** Close a file.
*/
int riscosClose(OsFile **pId){
  riscosFile *id = (riscosFile*)*pId;
  _kernel_swi_regs regs;

  regs.r[0] = OSFind_Close;
  regs.r[1] = ((riscosFile *)id)->h;

  _kernel_swi(OS_Find,&regs,&regs);
  if( ((riscosFile *)id)->delOnClose ){
    sqlite3RISCOSDelete(((riscosFile *)id)->pathToDel);
    sqliteFree(((riscosFile *)id)->pathToDel);
  }
  TRACE3("CLOSE    %-3d %d\n", id, ((riscosFile *)id)->h);
  sqliteFree(id);
  *pId = 0;
  return SQLITE_OK;
}

/*
** Get information to seed the random number generator.  The seed
** is written into the buffer zBuf[256].  The calling function must
** supply a sufficiently large buffer.
*/
int sqlite3RISCOSRandomSeed(char *zBuf){
  /* We have to initialize zBuf to prevent valgrind from reporting
  ** errors.  The reports issued by valgrind are incorrect - we would
  ** prefer that the randomness be increased by making use of the
  ** uninitialized space in zBuf - but valgrind errors tend to worry
  ** some users.  Rather than argue, it seems easier just to initialize
  ** the whole array and silence valgrind, even if that means less randomness
  ** in the random seed.
  **
  ** When testing, initializing zBuf[] to zero is all we do.  That means
  ** that we always use the same random number sequence.* This makes the
  ** tests repeatable.
  */
  memset(zBuf, 0, 256);
#if !defined(SQLITE_TEST)
  {
    time((time_t*)zBuf);
  }
#endif
  return SQLITE_OK;
}

/*
** Sleep for a little while.  Return the amount of time slept.
*/
int sqlite3RISCOSSleep(int ms){
  return ms;
}

/*
** Static variables used for thread synchronization
*/
static int inMutex = 0;
#ifdef SQLITE_UNIX_THREADS
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

/*
** The following pair of routine implement mutual exclusion for
** multi-threaded processes.  Only a single thread is allowed to
** executed code that is surrounded by EnterMutex() and LeaveMutex().
**
** SQLite uses only a single Mutex.  There is not much critical
** code and what little there is executes quickly and without blocking.
*/
void sqlite3RISCOSEnterMutex(){
  assert( !inMutex );
  inMutex = 1;
}
void sqlite3RISCOSLeaveMutex(){
  assert( inMutex );
  inMutex = 0;
}

/*
** Turn a relative pathname into a full pathname.  Return a pointer
** to the full pathname stored in space obtained from sqliteMalloc().
** The calling function is responsible for freeing this space once it
** is no longer needed.
*/
char *sqlite3RISCOSFullPathname(const char *zRelative){
  _kernel_swi_regs regs;

  char buf[1024];

  char *zFull = 0;

  regs.r[0] = OSFSControl_CanonicalisePath;
  regs.r[1] = (int)zRelative;
  regs.r[2] = (int)buf;
  regs.r[3] = 0;
  regs.r[4] = 0;
  regs.r[5] = sizeof(buf);

  _kernel_swi(OS_FSControl,&regs,&regs);

  sqlite3SetString(&zFull, buf, 0);

  return zFull;
}

/*
** The following variable, if set to a non-zero value, becomes the result
** returned from sqlite3RISCOSCurrentTime().  This is used for testing.
*/
#ifdef SQLITE_TEST
int sqlite3_current_time = 0;
#endif

/*
** Find the current time (in Universal Coordinated Time).  Write the
** current time and date as a Julian Day number into *prNow and
** return 0.  Return 1 if the time and date cannot be found.
*/
int sqlite3RISCOSCurrentTime(double *prNow){
  time_t t;
  time(&t);
  *prNow = t/86400.0 + 2440587.5;
  return 0;
}

#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
** Check that a given pathname is a directory and is writable
**
*/
int sqlite3RISCOSIsDirWritable(char *zBuf){
  _kernel_swi_regs regs;

  if( zBuf==0 ) return 0;
  if( zBuf[0]==0 ) return 0;

  regs.r[0] = OSFile_ReadStampedNoPath;
  regs.r[1] = (int)zBuf;

  _kernel_swi(OS_File,&regs,&regs);

  if (regs.r[0] != osfile_IS_DIR) {
    return 0;
  }

  return 1;
}

#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

/*
** Return TRUE if the mutex is currently held.
**
** If the thisThrd parameter is true, return true only if the
** calling thread holds the mutex.  If the parameter is false, return
** true if any thread holds the mutex.
*/
int sqlite3RISCOSInMutex(int thisThrd){
  return inMutex>0;
}

ThreadData *sqlite3RISCOSThreadSpecificData(int allocateFlag){
  static const ThreadData zeroData = {0};  /* Initializer to silence warnings

                                           ** from broken compilers */
return NULL;
}


#if 0 /* NOT USED */
/*
** Find the time that the file was last modified.  Write the
** modification time and date as a Julian Day number into *prNow and
** return SQLITE_OK.  Return SQLITE_ERROR if the modification
** time cannot be found.
*/
int sqlite3RISCOSFileModTime(OsFile *id, double *prNow){
  int rc;
  struct stat statbuf;
  if( fstat(id->h, &statbuf)==0 ){
    *prNow = statbuf.st_mtime/86400.0 + 2440587.5;
    rc = SQLITE_OK;
  }else{
    rc = SQLITE_ERROR;
  }
  return rc;
}
#endif /* NOT USED */

/*
** Change the value of the fullsync flag in the given file descriptor.
*/
static void riscosSetFullSync(OsFile *id, int v){
}

/*
** Return the underlying file handle for an OsFile
*/
static int riscosFileHandle(OsFile *id){
  return ((riscosFile*)id)->h;
}

/*
** Return an integer that indices the type of lock currently held
** by this handle.  (Used for testing and analysis only.)
*/
static int riscosLockState(OsFile *id){
  return ((riscosFile*)id)->locktype;
}

/*
** This vector defines all the methods that can operate on an OsFile
** for riscos.
*/
static const IoMethod sqlite3RISCOSIoMethod = {
  riscosClose,
  riscosOpenDirectory,
  riscosRead,
  riscosWrite,
  riscosSeek,
  riscosTruncate,
  riscosSync,
  riscosSetFullSync,
  riscosFileHandle,
  riscosFileSize,
  riscosLock,
  riscosUnlock,
  riscosLockState,
  riscosCheckReservedLock,
};

/*
** Allocate memory for a riscosFile.  Initialize the new riscosFile
** to the value given in pInit and return a pointer to the new
** OsFile.  If we run out of memory, close the file and return NULL.
*/
static int allocateRISCOSFile(riscosFile *pInit, OsFile **pId){
  riscosFile *pNew;
  pInit->locktype = 0;
  pNew = malloc( sizeof(riscosFile) );
  if( pNew==0 ){
    *pId = 0;
    return SQLITE_NOMEM;
  }else{
    *pNew = *pInit;
    pNew->pMethod = &sqlite3RISCOSIoMethod;
    *pId = (OsFile*)pNew;
    OpenCounter(+1);
    return SQLITE_OK;
  }
}

#endif /* OS_UNIX */
